#####################################################################
#
# WEB APP FOR RFID READER SDK
# AUTHOR: zengkai001@qq.com
# DATE: 2025-01-24
#
#####################################################################

import sys
import time
import logging
from logging.handlers import TimedRotatingFileHandler, RotatingFileHandler
import os
from datetime import datetime

# Configure logging
def setup_logging():
    # Create logs directory if not exists
    log_dir = os.path.join(os.path.dirname(__file__), 'logs')
    os.makedirs(log_dir, exist_ok=True)
    
    # Main logger
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    
    # Log format
    formatter = logging.Formatter(
        '%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(funcName)s - %(message)s'
    )
    
    # Console handler
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)
    
    # File handler with time and size rotation
    log_file = os.path.join(log_dir, 'app.log')
    file_handler = TimedRotatingFileHandler(
        log_file, when='midnight', interval=1, backupCount=30
    )
    file_handler.suffix = "%Y-%m-%d.log"
    file_handler.setLevel(logging.DEBUG)
    file_handler.setFormatter(formatter)
    
    # Add size rotation
    size_handler = RotatingFileHandler(
        log_file, maxBytes=20*1024*1024, backupCount=30
    )
    size_handler.setLevel(logging.DEBUG)
    size_handler.setFormatter(formatter)
    
    logger.addHandler(file_handler)
    logger.addHandler(size_handler)

setup_logging()
from pathlib import Path
from flask import Flask, render_template, request, redirect, flash, jsonify
from flask_cors import CORS

# Add PYTHON_SDK to path
sys.path.append(str(Path(__file__).parent.parent / 'PYTHON_SDK'))
from rfid_reader import RFIDReader
from config_tool import *

# RFID Process Manager
import multiprocessing as mp
from multiprocessing import Queue

class RFIDProcess:
    def __init__(self):
        self.process = None
        self.task_queue = Queue()
        self.result_queue = Queue()
        
    def start(self):
        if self.process is None:
            self.process = mp.Process(
                target=self._worker,
                daemon=True
            )
            self.process.start()
    
    def _worker(self):
        from rfid_reader import RFIDReader
        from pathlib import Path
        
        # Initialize reader in worker process
        reader = RFIDReader(str(Path(__file__).parent.parent / 'PYTHON_SDK/x86_64/libGZAE_rfid.so'))
        connected = False
        
        while True:
            task = self.task_queue.get()
            if task['cmd'] == 'open':
                try:
                    reader.open_device(task['conn_str'])
                    connected = True
                    self.result_queue.put({'success': True})
                except Exception as e:
                    self.result_queue.put({'success': False, 'error': str(e)})
                    
            elif task['cmd'] == 'inventory':
                try:
                    if not connected:
                        self.result_queue.put({'success': False, 'error': 'Device not connected'})
                        continue
                        
                    reader.start_inventory()
                    tags = reader.get_tag_info()
                    self.result_queue.put({
                        'success': True,
                        'tags': tags['tags']
                    })
                except Exception as e:
                    self.result_queue.put({'success': False, 'error': str(e)})
                    
            elif task['cmd'] == 'device_info':
                try:
                    if not connected:
                        self.result_queue.put({'success': False, 'error': 'Device not connected'})
                        continue
                        
                    info = reader.get_device_info()
                    self.result_queue.put({
                        'success': True,
                        'info': info
                    })
                except Exception as e:
                    self.result_queue.put({'success': False, 'error': str(e)})
                    
            elif task['cmd'] == 'close':
                try:
                    if not connected:
                        self.result_queue.put({'success': False, 'error': 'Device not connected'})
                        continue
                        
                    reader.close_device()
                    connected = False
                    self.result_queue.put({'success': True})
                except Exception as e:
                    self.result_queue.put({'success': False, 'error': str(e)})
                    
            elif task['cmd'] == 'read_tag':
                try:
                    if not connected:
                        self.result_queue.put({'success': False, 'error': 'Device not connected'})
                        continue
                        
                    tag_data = reader.read_tag(
                        task['uid'],
                        task['block_start'],
                        task['block_count'],
                        task['access_pwd'],
                        task['mem_type']
                    )
                    if tag_data is None:
                        self.result_queue.put({'success': False, 'error': 'Failed to read tag'})
                    else:
                        self.result_queue.put({
                            'success': True,
                            'data': tag_data.hex()
                        })
                except Exception as e:
                    self.result_queue.put({'success': False, 'error': str(e)})
                    
            elif task['cmd'] == 'write_tag':
                try:
                    if not connected:
                        self.result_queue.put({'success': False, 'error': 'Device not connected'})
                        continue
                        
                    success = reader.write_tag(
                        task['uid'],
                        task['block_start'],
                        bytes.fromhex(task['data']),
                        task['access_pwd'],
                        task['mem_type']
                    )
                    self.result_queue.put({
                        'success': success,
                        'error': None if success else 'Failed to write tag'
                    })
                except Exception as e:
                    self.result_queue.put({'success': False, 'error': str(e)})

rfid_process = RFIDProcess()
rfid_process.start()

app = Flask(__name__)
app.secret_key = 'dev'  # Required for flash messages
CORS(app, resources={
    r"/*": {
        "origins": "*",
        "methods": ["GET", "POST", "OPTIONS"],
        "allow_headers": ["Content-Type"]
    }
})

@app.route('/')
def rfid():
    return render_template('rfid.html')

@app.route('/open-device', methods=['POST'])
def open_device():
    logging.info("Opening device - Request received")
    try:
        # 获取连接字符串
        c = read_config()
        ReaderConnStr = c['ReaderConnStr']
        
        # 发送打开设备命令到工作进程
        rfid_process.task_queue.put({
            'cmd': 'open',
            'conn_str': ReaderConnStr
        })
        
        # 获取结果(设置5秒超时)
        try:
            result = rfid_process.result_queue.get(timeout=5)
            if result['success']:
                logging.info("Device opened successfully")
                return {
                    'error': False,
                    'message': "Device opened successfully"
                }
            else:
                logging.error(f"Error opening device: {result['error']}")
                return {
                    'error': True,
                    'message': f"Error opening device: {result['error']}"
                }
        except mp.queues.Empty:
            logging.error("Device open timeout")
            return {
                'error': True,
                'message': "Device open timeout"
            }
    except Exception as e:
        logging.error(f"Error opening device: {str(e)}", exc_info=True)
        return {
            'error': True,
            'message': f"Error opening device: {str(e)}"
        }

@app.route('/device-info', methods=['POST'])
def device_info():
    logging.info("Getting device info - Request received")
    try:
        # Send device info request to worker process
        rfid_process.task_queue.put({'cmd': 'device_info'})
        
        # Get result (5 second timeout)
        try:
            result = rfid_process.result_queue.get(timeout=5)
            if result['success']:
                logging.info(f"Device info retrieved: {result['info']}")
                return {
                    'error': False,
                    'message': f"Device Info: {result['info']}"
                }
            else:
                logging.error(f"Error getting device info: {result['error']}")
                return {
                    'error': True,
                    'message': f"Error getting device info: {result['error']}"
                }
        except mp.queues.Empty:
            logging.error("Device info timeout")
            return {
                'error': True,
                'message': "Device info timeout"
            }
    except Exception as e:
        logging.error(f"Error getting device info: {str(e)}", exc_info=True)
        return {
            'error': True,
            'message': f"Error getting device info: {str(e)}"
        }

@app.route('/close-device', methods=['POST'])
def close_device():
    logging.info("Closing device - Request received")
    try:
        # Send close command to worker process
        rfid_process.task_queue.put({'cmd': 'close'})
        
        # Get result (5 second timeout)
        try:
            result = rfid_process.result_queue.get(timeout=5)
            if result['success']:
                logging.info("Device closed successfully")
                return {
                    'error': False,
                    'message': "Device closed successfully"
                }
            else:
                logging.error(f"Error closing device: {result['error']}")
                return {
                    'error': True,
                    'message': f"Error closing device: {result['error']}"
                }
        except mp.queues.Empty:
            logging.error("Close device timeout")
            return {
                'error': True,
                'message': "Close device timeout"
            }
    except Exception as e:
        logging.error(f"Error closing device: {str(e)}", exc_info=True)
        return {
            'error': True,
            'message': f"Error closing device: {str(e)}"
        }

@app.route('/inventory', methods=['POST'])
def inventory():
    """通过工作进程执行盘点操作"""
    logging.info("Inventory request received")
    try:
        # 检查是否请求TID数据 (处理空/无效请求体情况)
        try:
            tid_requested = request.json.get('tid', False) if request.json else False
        except:
            tid_requested = False
        
        # 发送盘点任务到工作进程
        rfid_process.task_queue.put({'cmd': 'inventory'})
        
        # 获取结果(设置5秒超时)
        try:
            result = rfid_process.result_queue.get(timeout=5)
            if result['success']:
                if tid_requested:
                    # 对于每个标签，读取TID数据
                    tag_data = []
                    for tag in result['tags']:
                        # 发送读取TID任务到工作进程
                        rfid_process.task_queue.put({
                            'cmd': 'read_tag',
                            'uid': tag['EPC'],
                            'block_start': 0,
                            'block_count': 6,
                            'access_pwd': '00000000',
                            'mem_type': 2  # TID memory
                        })
                        tid_result = rfid_process.result_queue.get(timeout=5)
                        if tid_result['success']:
                            tag_data.append([tag['EPC'], tid_result['data']])
                        else:
                            tag_data.append([tag['EPC'], 'Error reading TID'])
                    
                    logging.info(f"Inventory with TID successful - {len(tag_data)} tags")
                    return {
                        'error': False,
                        'message': "Inventory with TID successful",
                        'tags': tag_data
                    }
                else:
                    # 普通盘点只返回EPC
                    epc_list = [tag['EPC'] for tag in result['tags']]
                    logging.info(f"Inventory successful - {len(epc_list)} tags")
                    return {
                        'error': False,
                        'message': "Inventory successful",
                        'epcs': epc_list
                    }
            else:
                logging.error(f"Inventory failed: {result['error']}")
                return {
                    'error': True,
                    'message': f"Inventory failed: {result['error']}"
                }
        except mp.queues.Empty:
            logging.error("Inventory timeout")
            return {
                'error': True,
                'message': "Inventory timeout"
            }
    except Exception as e:
        logging.error(f"Error during inventory: {str(e)}", exc_info=True)
        return {
            'error': True,
            'message': f"Error during inventory: {str(e)}"
        }

@app.route('/read-tag', methods=['POST'])
def read_tag():
    """
    Read data from a tag.
    """
    logging.info("Read tag request received")
    try:
        # Get parameters from request
        data = request.json
        rfid_process.task_queue.put({
            'cmd': 'read_tag',
            'uid': data.get('uid'),
            'block_start': data.get('block_start', 0),
            'block_count': data.get('block_count', 1),
            'access_pwd': data.get('access_pwd', '00000000'),
            'mem_type': data.get('mem_type', 3)
        })
        
        # Get result (5 second timeout)
        try:
            result = rfid_process.result_queue.get(timeout=5)
            if result['success']:
                logging.info(f"Tag read successfully - UID: {data.get('uid')}")
                return {
                    'error': False,
                    'message': "Tag read successfully",
                    'data': result['data']
                }
            else:
                logging.error(f"Error reading tag: {result['error']}")
                return {
                    'error': True,
                    'message': f"Error reading tag: {result['error']}"
                }
        except mp.queues.Empty:
            logging.error("Read tag timeout")
            return {
                'error': True,
                'message': "Read tag timeout"
            }
    except Exception as e:
        logging.error(f"Error reading tag: {str(e)}", exc_info=True)
        return {
            'error': True,
            'message': f"Error reading tag: {str(e)}"
        }

@app.route('/write-tag', methods=['POST'])
def write_tag():
    """
    Write data to a tag.
    """
    logging.info("Write tag request received")
    try:
        # Get parameters from request
        data = request.json
        rfid_process.task_queue.put({
            'cmd': 'write_tag',
            'uid': data.get('uid'),
            'block_start': data.get('block_start', 0),
            'data': data.get('data', ''),
            'access_pwd': data.get('access_pwd', '00000000'),
            'mem_type': data.get('mem_type', 3)
        })
        
        # Get result (5 second timeout)
        try:
            result = rfid_process.result_queue.get(timeout=5)
            if result['success']:
                logging.info(f"Tag written successfully - UID: {data.get('uid')}")
                return {
                    'error': False,
                    'message': "Tag written successfully"
                }
            else:
                logging.error(f"Error writing tag: {result['error']}")
                return {
                    'error': True,
                    'message': f"Error writing tag: {result['error']}"
                }
        except mp.queues.Empty:
            logging.error("Write tag timeout")
            return {
                'error': True,
                'message': "Write tag timeout"
            }
    except Exception as e:
        logging.error(f"Error writing tag: {str(e)}", exc_info=True)
        return {
            'error': True,
            'message': f"Error writing tag: {str(e)}"
        }

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5005, debug=True)
